﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace LaserTag.Common.Controls
{

        #region ImageSequencer

        public class ImageSequencer : FrameworkElement, ISupportInitialize
        {
            #region Fields

            DispatcherTimer timer;
            static Random R = new Random();

            #endregion

            #region Constructor

            public ImageSequencer()
            {
                timer = new DispatcherTimer(DispatcherPriority.Render);
                timer.Interval = TimeSpan.FromMilliseconds((int)Math.Abs((1000.0 / (double)FrameRate)));
                timer.Tick += new EventHandler(timer_Tick);
                timer.Start();
            }

            #endregion

            #region Private Methods

            private int CalculateFrameOffset(int from, int offset)
            {
                if (ImageSequence != null && ImageSequence.Count > 0)
                {
                    int newFrom = from;
                    int newOffset = offset;
                    int newIndex = newFrom + newOffset;

                    while (newIndex < 0)
                    {
                        newFrom = ImageSequence.Count - 1;
                        newOffset = newIndex;
                        newIndex = newFrom + newOffset;
                    }

                    while (newIndex > (ImageSequence.Count - 1))
                    {
                        newFrom = 0;
                        newOffset = (newIndex - (ImageSequence.Count - 1));
                        newIndex = newFrom + newOffset;
                    }

                    return newIndex;
                }
                else
                {
                    return 0;
                }
            }

            #endregion

            #region Overridden Methods

            protected override void OnRender(DrawingContext drawingContext)
            {
                if (ImageSequence != null && ImageSequence.Count > 0)
                {
                    int frameDirection = -1;

                    if ((SequenceDirection == SequenceDirection.Forward && FrameRate > 0) || (SequenceDirection == SequenceDirection.Backward && FrameRate < 0))
                    {
                        frameDirection = 1;
                    }

                    for (int i = 0; i < FrameTrailCount; i++)
                    {
                        double opacity = (double)(i + 1) / (double)FrameTrailCount;
                        int index = CalculateFrameOffset(CurrentFrame, (frameDirection * FrameTrailCount - (i + 1)));

                        drawingContext.PushOpacity(opacity);
                        drawingContext.DrawImage(ImageSequence[index], new Rect(0, 0, this.ActualWidth, this.ActualHeight));
                        drawingContext.Pop();
                    }

                    CurrentFrame = CalculateFrameOffset(CurrentFrame, frameDirection);
                    if (_waitingForPause && (CurrentFrame == _pauseFrame)) this.Pause();

                    base.OnRender(drawingContext);
                }
            }

            #endregion

            #region Event Handlers

            void timer_Tick(object sender, EventArgs e)
            {
                this.InvalidateVisual();
            }

            #endregion

            #region Properties

            #region SequenceDirection

            public static readonly DependencyProperty SequenceDirectionProperty =
                DependencyProperty.Register("SequenceDirection", typeof(SequenceDirection), typeof(ImageSequencer),
                    new FrameworkPropertyMetadata((SequenceDirection)SequenceDirection.Forward,
                        FrameworkPropertyMetadataOptions.None));

            public SequenceDirection SequenceDirection
            {
                get { return (SequenceDirection)GetValue(SequenceDirectionProperty); }
                set { SetValue(SequenceDirectionProperty, value); }
            }

            #endregion

            #region CurrentFrame

            public static readonly DependencyProperty CurrentFrameProperty =
                DependencyProperty.Register("CurrentFrame", typeof(int), typeof(ImageSequencer),
                    new FrameworkPropertyMetadata((int)0,
                        new PropertyChangedCallback(OnCurrentFrameChanged)));

            public int CurrentFrame
            {
                get { return (int)GetValue(CurrentFrameProperty); }
                set { SetValue(CurrentFrameProperty, value); }
            }

            private static void OnCurrentFrameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                ((ImageSequencer)d).OnCurrentFrameChanged(e);
            }

            protected virtual void OnCurrentFrameChanged(DependencyPropertyChangedEventArgs e)
            {
            }

            #endregion

            #region FrameRate

            public static readonly DependencyProperty FrameRateProperty =
                DependencyProperty.Register("FrameRate", typeof(int), typeof(ImageSequencer),
                    new FrameworkPropertyMetadata((int)60,
                        new PropertyChangedCallback(OnFrameRateChanged)));

            public int FrameRate
            {
                get { return (int)GetValue(FrameRateProperty); }
                set { SetValue(FrameRateProperty, value); }
            }

            private static void OnFrameRateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                ((ImageSequencer)d).OnFrameRateChanged(e);
            }


            protected virtual void OnFrameRateChanged(DependencyPropertyChangedEventArgs e)
            {
                if ((int)e.NewValue == 0)
                {
                    timer.Stop();
                }
                else
                {
                    if (!_isPaused) timer.Start();
                    timer.Interval = TimeSpan.FromMilliseconds((int)Math.Abs((1000 / (double)((int)e.NewValue))));
                }
            }

            #endregion

            #region FrameTrailCount

            public static readonly DependencyProperty FrameTrailCountProperty =
                DependencyProperty.Register("FrameTrailCount", typeof(int), typeof(ImageSequencer),
                    new FrameworkPropertyMetadata((int)1));

            public int FrameTrailCount
            {
                get { return (int)GetValue(FrameTrailCountProperty); }
                set { SetValue(FrameTrailCountProperty, value); }
            }

            #endregion

            #region ImageSequence

            public static readonly DependencyProperty ImageSequenceProperty =
                DependencyProperty.Register("ImageSequence", typeof(ImageSequence), typeof(ImageSequencer),
                    new FrameworkPropertyMetadata((ImageSequence)null));

            public ImageSequence ImageSequence
            {
                get { return (ImageSequence)GetValue(ImageSequenceProperty); }
                set { SetValue(ImageSequenceProperty, value); }
            }

            #endregion

            #region SourceFolder

            public static readonly DependencyProperty SourceFolderProperty =
                DependencyProperty.Register("SourceFolder", typeof(string), typeof(ImageSequencer),
                    new FrameworkPropertyMetadata((string)null,
                        new PropertyChangedCallback(OnSourceFolderChanged)));

            public string SourceFolder
            {
                get { return (string)GetValue(SourceFolderProperty); }
                set { SetValue(SourceFolderProperty, value); }
            }

            private static void OnSourceFolderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                ((ImageSequencer)d).OnSourceFolderChanged(e);
            }

            protected virtual void OnSourceFolderChanged(DependencyPropertyChangedEventArgs e)
            {
                if (this.ImageSequence == null)
                {
                    ImageSequence seq = new ImageSequence();

                    string folder = e.NewValue as string;
                    if (Directory.Exists(folder))
                    {
                        foreach (string filename in Directory.GetFiles(folder, "*.png"))
                        {
                            ImageSource img = new BitmapImage(new Uri(filename, UriKind.RelativeOrAbsolute));
                            seq.Add(img);
                        }
                    }

                    this.ImageSequence = seq;
                }
            }

            #endregion

            #endregion

            #region Public Methods

            bool _isPaused = false;
            public void Pause()
            {
                timer.Stop();
                _isPaused = true;
                _waitingForPause = false;
            }

            bool _waitingForPause = false;
            int _pauseFrame = 0;
            public void PauseOnFrame(int frame)
            {
                _waitingForPause = true;
                _pauseFrame = frame;
            }

            public void Play()
            {
                timer.Start();
                _isPaused = false;
            }

            #endregion

        }

        #endregion

        #region ImageSequence

        public class ImageSequence : List<ImageSource>, INotifyPropertyChanged, ISupportInitialize
        {
            #region Private Fields

            bool isInitializing = false;

            #endregion

            #region Properties

            private string _source = null;
            public string Source
            {
                get { return _source; }
                set
                {
                    _source = value;
                    if (!isInitializing) this.LoadImages();
                    RaisePropertyChanged("Source");
                }
            }

            private int _totalFrames = 0;
            public int TotalFrames
            {
                get { return _totalFrames; }
                set
                {
                    _totalFrames = value;
                    RaisePropertyChanged("TotalFrames");
                }
            }

            #endregion

            #region Private Methods

            private void LoadImages()
            {
                for (int i = 0; i < _totalFrames; i++)
                {
                    string src = String.Format(_source, i);
                    ImageSource img = new BitmapImage(new Uri(src, UriKind.RelativeOrAbsolute));
                    this.Add(img);
                }
            }

            #endregion

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;
            protected void RaisePropertyChanged(string name)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
            }

            #endregion

            #region ISupportInitialize Members

            public void BeginInit()
            {
                isInitializing = true;
            }

            public void EndInit()
            {
                isInitializing = false;
                LoadImages();
            }

            #endregion
        }

        #endregion

        #region SequenceDirection

        public enum SequenceDirection
        {
            Forward, Backward
        }

        #endregion
    
}
